package com.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.entity.CollegeSort;
import com.entity.Student;
import com.service.StudentService;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 继承AnalysisEventListener读取数据的监听器
 * 用于控制数据数量、类型等
 */
public class StudentListener extends AnalysisEventListener<StudentExcel> {
    // 设置list初始容量为100000
    private List<Student> studentList = new ArrayList<>(100000);
    /**
     * CollegeSort是学生中的班级-专业-学院
     * 用于判断是否有这个学院
     * 学院中是否有这个专业
     * 专业是否有这个班级
     */
    private Map<String, CollegeSort> map;
    // 设置添加数据的最大量
    private static final int BATCH_COUNT = 800;
    // 线程数量
    private static final int THREAD_COUNT = 5;
    // 线程池
    private ExecutorService pool;
    // 行号
    private int line = 1;
    // 学生学号，判断学号是否重复
    private List<String> studentNumberList;
    // 返回响应，提示用户哪里错误
    private HttpServletResponse response;
    // 逻辑层，用于批量添加数据
    private StudentService studentService;
    // 用于控制线程工作
    private CountDownLatch countDownLatch;
    // 身份证，用于判断是否重复
    private List<String> identityList;
    // 判断是否可以读取下一个数据
    private boolean isGo = true;

    // 构造方法
    public StudentListener(Map<String, CollegeSort> map, List<String> list, List<String> identityList, HttpServletResponse response, StudentService studentService) {
        this.map = map;
        this.pool = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(120), new ThreadPoolExecutor.AbortPolicy());
        this.studentNumberList = list;
        this.response = response;
        this.studentService = studentService;
        this.countDownLatch = new CountDownLatch(THREAD_COUNT);
        this.identityList = identityList;
    }


    /**
     * 重写父类的方法，每读到一条数据就进行封装到Student类中，用于插入mysql中
     */
    @Override
    public void invoke(StudentExcel student, AnalysisContext analysisContext) {
        line++;
        response.setCharacterEncoding("UTF-8");
        try(PrintWriter writer = response.getWriter();){
            String number = student.getNumber();
            if (studentNumberList.contains(number)){
                writer.write("第" + line + "行,第5列的" + number + "已存在！");
                throw new ExcelAnalysisStopException("第" + line + "行,第5列的" + number + "已存在！");
            }
            studentNumberList.add(number);
            String identity = student.getIdentity();
            if (identityList.contains(identity)){
                writer.write("第" + line + "行,第6列的" + identity + "已存在！");
                throw new ExcelAnalysisStopException("第" + line + "行,第6列的" + identity + "已存在！");
            }
            identityList.add(identity);
            String collegeName = student.getCollegeName();
            if (!map.containsKey(collegeName)){
                writer.write("第" + line + "行,第2列的" + collegeName + "不存在！");
                throw new ExcelAnalysisStopException("第" + line + "行,第2列的" + collegeName + "不存在！");
            }
            String majorName = student.getMajorName();
            CollegeSort sort = map.get(collegeName);
            if (!sort.getMajor().getmName().equals(majorName)){
                writer.write("第" + line + "行,第3列的" + majorName + "不存在！");
                throw new ExcelAnalysisStopException("第" + line + "行,第3列的" + majorName + "不存在！");
            }
            String className = student.getClassName();
            if (!sort.getClassGrade().getcName().equals(className)){
                writer.write("第" + line + "行,第4列的" + className + "不存在！");
                throw new ExcelAnalysisStopException("第" + line + "行,第4列的" + className + "不存在！");
            }
        }catch (Exception e){
            this.isGo = false;
            pool.shutdownNow();
            e.printStackTrace();
            return;
        }
        CollegeSort sort = map.get(student.getCollegeName());
        Student s = new Student();
        s.setState(student.getState());
        s.setClassId(sort.getClassGrade().getId());
        s.setNumber(student.getNumber());
        s.setReportDate(student.getReportDate());
        s.setIdentity(student.getIdentity());
        s.setPhotograph(student.getPhotograph());
        // 添加到List数据中
        studentList.add(s);
    }

    @Override
    public boolean hasNext(AnalysisContext context) {
        return this.isGo;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("开始读取");
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("读取完成");
        // 判断数据是否超过800，如果超出，就通过线程分别插入
        // mysql插入数据如果大于六万多就会报错
        // 所以要分批插入
        int n = (line-1) / BATCH_COUNT;
        try {
            for (int i = 1; i <= n; i++) {
                int j = i;
                pool.execute(() ->{
                    int ok = studentService.insertBatchStudent(studentList.subList((j - 1) * 800, j * 800));
                    System.out.println(ok);
                    countDownLatch.countDown();
                });
            }
            // 判断是否还有剩余数据
            if (studentList.size() != 0 && (line-1) % 800 != 0){
                int ok = studentService.insertBatchStudent(studentList.subList(n * 800, line - 1));
                System.out.println(ok);
                countDownLatch.countDown();
            }
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 不接收任务，等待线程执行完毕
            pool.shutdown();
        }
    }
}
